
import { ExecutiveContext, State, Executive, ExecuteStatus, ActivityStatus } from "../defination"
import { ActivityRecord, ByteArray, ExecutiveRecord } from "../entity"
import {  } from "../repository"
import { ACTIVITY, ACTIVITY_STATUS, DEALER, EXECUTION_RECORD, NEXT, REQUEST, REQUEST_ID, STATE_RECORD } from "../shared-symbols"
import { BaseAction } from "./action"
import type { AutherizeAction } from "./autherizeAction"
import { EnterAction } from "./enterAction"
import { DealAction } from "./dealAction"
import { ExitAction } from "./exitAction"


/**
 * the begin action
 * it is after the autherize action
 * it main do these:
 * 1. create the execution record
 * 2. use beginActivity() to try to lock the activity by the requestId
 * 3. if the activity is locked successfully, then update the activity executeStatus to RUNNING
 * 5. if the activity is locked successfully, then return the next action
 * 6. if the activity is not locked successfully,throw the RepositoryLockError
 * 7. the next() will return different actions based on the activity status
 * @export
 * @class BeginAction
 * @extends {BaseAction<TActivity, TState, TRecord, TContext>}
 * @template TActivity
 * @template TState
 * @template TRecord
 * @template TContext
 */
export class BeginAction
<
  TActivity extends Executive<TActivity,TState,TRecord,TContext>,
  TState extends State=State,
  TRecord extends ExecutiveRecord=ExecutiveRecord,
  TContext extends ExecutiveContext=ExecutiveContext
> extends BaseAction<TActivity,TState,TRecord,TContext>
{
  
  constructor(act:AutherizeAction<TActivity,TState,TRecord,TContext>){
    super(act)
  }
  async invoke(context:TContext):Promise<BaseAction<TActivity,TState,TRecord,TContext>|null|undefined>{
    const stateRecord = this[STATE_RECORD] as unknown as ActivityRecord
    stateRecord.requestId = context[REQUEST_ID]
    stateRecord.lockTime = context.calendar.now()
    stateRecord.lockExpiryTime = new Date(stateRecord.lockTime.getTime() + (context.lockTimeout??60000))
    const executionRecord =  {
      id:undefined!,
      activityId:this[STATE_RECORD].id,
      dealerId:context[DEALER].id,
      startTime:context.calendar.now(),
      overTime:undefined,
      activityStatus:stateRecord.activityStatus,
      executeStatus:context.enumConverter.toByteArray<ByteArray>(ExecuteStatus.RUNNING,ExecuteStatus,true)!,
      requestId:context[REQUEST_ID],
      graphId:stateRecord.graphId,
      name:stateRecord.name,
      path:stateRecord.path,
      parentId:stateRecord.parentId,
      flowId:stateRecord.flowId,
      originalActivityStatus:stateRecord.activityStatus,
      updateTime:context.calendar.now(),
      input:undefined!,
      variable:stateRecord.variable,
      output:undefined!,  
      error:undefined!
    }
    const eRecord = await context.repository.beginActivity(stateRecord,executionRecord,context[REQUEST].requestId)
    this[EXECUTION_RECORD] = eRecord
    return this.next
  }
  get next():BaseAction<TActivity,TState,TRecord,TContext>|null|undefined{
    if (this[EXECUTION_RECORD]===undefined)
      return undefined
    if (this[NEXT]===undefined) {
      const activityStatus = this[ACTIVITY][ACTIVITY_STATUS]
      if (activityStatus === ActivityStatus.INITIALIZED){
        return this[NEXT] = new EnterAction(this)
      }
      if (activityStatus === ActivityStatus.ENTERED || activityStatus === ActivityStatus.SUSPENDED){
        return this[NEXT] = new DealAction(this)
      }
      if (activityStatus === ActivityStatus.DONE){
        return this[NEXT] = new ExitAction(this)
      }
      return this[NEXT] = null
    }
    
    return this[NEXT]
  }
}